package com.easy.cloud.web.service.upms.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import com.easy.cloud.web.component.core.constants.GlobalCommonConstants;
import com.easy.cloud.web.component.core.enums.HttpResultEnum;
import com.easy.cloud.web.component.core.enums.StatusEnum;
import com.easy.cloud.web.component.core.exception.BusinessException;
import com.easy.cloud.web.component.core.tenant.TenantBroker;
import com.easy.cloud.web.component.core.util.BeanUtils;
import com.easy.cloud.web.component.mysql.query.SpecificationWrapper;
import com.easy.cloud.web.component.security.util.SecurityUtils;
import com.easy.cloud.web.service.upms.api.dto.*;
import com.easy.cloud.web.service.upms.api.enums.SocialTypeEnum;
import com.easy.cloud.web.service.upms.api.vo.UserVO;
import com.easy.cloud.web.service.upms.biz.constant.UpmsCacheConstants;
import com.easy.cloud.web.service.upms.biz.converter.DeptConverter;
import com.easy.cloud.web.service.upms.biz.converter.UserConverter;
import com.easy.cloud.web.service.upms.biz.domain.MenuDO;
import com.easy.cloud.web.service.upms.biz.domain.RoleDO;
import com.easy.cloud.web.service.upms.biz.domain.UserDO;
import com.easy.cloud.web.service.upms.biz.repository.DeptRepository;
import com.easy.cloud.web.service.upms.biz.repository.MenuRepository;
import com.easy.cloud.web.service.upms.biz.repository.RoleRepository;
import com.easy.cloud.web.service.upms.biz.repository.UserRepository;
import com.easy.cloud.web.service.upms.biz.service.IUserService;
import com.easy.cloud.web.service.upms.biz.social.ISocialService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Page;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

/**
 * User 业务逻辑
 *
 * @author Fast Java
 * @date 2023-08-03 14:25:41
 */
@Slf4j
@Service
@AllArgsConstructor
public class UserServiceImpl implements IUserService, ApplicationContextAware {

    private final EnumMap<SocialTypeEnum, ISocialService> socialServices = new EnumMap<>(SocialTypeEnum.class);

    private final UserRepository userRepository;

    private final RoleRepository roleRepository;

    private final DeptRepository deptRepository;

    private final MenuRepository menuRepository;

    private final PasswordEncoder passwordEncoder;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        applicationContext.getBeansOfType(ISocialService.class).values()
                .forEach(socialService -> socialServices.put(socialService.getType(), socialService));
    }

    /**
     * 构建用户信息在存储之前
     *
     * @param userDTO 用户入参信息
     * @param userDO  用户存储信息
     */
    private void buildUserBeforeStore(UserDTO userDTO, UserDO userDO) {
        // 获取角色编码
        Set<String> roleCodes = Optional.ofNullable(userDTO.getRoleCodes()).orElse(new HashSet<>());
        // 移除超级管理员角色,新增用户禁止指派超级管理员角色
        roleCodes.remove(GlobalCommonConstants.SUPER_ADMIN_ROLE);
        // 根据账号或电话获取用户下信息
        List<UserDO> users = userRepository.findByUserNameOrTel(userDTO.getUserName(), userDTO.getTel());
        // ID不为空
        if (StrUtil.isNotBlank(userDTO.getId())) {
            users.stream().filter(user -> !user.getId().equals(userDTO.getId()))
                    .findFirst().orElseThrow(() -> new BusinessException("当前账号或电话已存在"));
        } else if (CollUtil.isNotEmpty(users)) {
            // ID为空，用户信息不为空，则表示重复
            throw new BusinessException("当前账号或电话已存在");
        }

        // 角色编码不为空
        if (CollUtil.isNotEmpty(userDTO.getRoleCodes())) {
            List<RoleDO> userRoles = roleRepository.findAllByCodeIn(CollUtil.newArrayList(userDTO.getRoleCodes()));
            userDO.setRoles(userRoles);
        }

        // 获取部门ID
        if (StrUtil.isNotBlank(userDTO.getDeptId())) {
            // 设置部门信息
            deptRepository.findById(userDTO.getDeptId()).ifPresent(userDO::setDept);
        }

        // 密码编译
        userDO.setPassword(passwordEncoder.encode(userDTO.getPassword()));
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    public UserVO save(UserDTO userDTO) {
        // 转换成DO对象
        UserDO user = UserConverter.convertTo(userDTO);
        // TODO 校验逻辑
        // 若昵称为空，则根据账号或电话生成默认昵称
        if (StrUtil.isBlank(userDTO.getNickName())) {
            // 昵称=账号
            user.setNickName(userDTO.getUserName());
        }

        // 构建用户信息在存储之前
        this.buildUserBeforeStore(userDTO, user);
        // 存储
        userRepository.save(user);
        // 转换对象
        return UserConverter.convertTo(user);
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    @CacheEvict(value = UpmsCacheConstants.USER_DETAILS, allEntries = true)
    public UserVO update(UserDTO userDTO) {
        // 转换成DO对象
        if (Objects.isNull(userDTO.getId())) {
            throw new RuntimeException("当前更新对象ID为空");
        }

        // TODO 业务逻辑校验
        UserDO user = userRepository.findById(userDTO.getId())
                .orElseThrow(() -> new BusinessException("当前用户信息不存在"));
        // 将修改的数据赋值给数据库数据
        BeanUtils.copyProperties(userDTO, user, true);
        // 构建用户信息在存储之前
        this.buildUserBeforeStore(userDTO, user);
        // 更新
        userRepository.save(user);
        // 转换对象
        return UserConverter.convertTo(user);
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    @CacheEvict(value = UpmsCacheConstants.USER_DETAILS, allEntries = true)
    public Boolean removeById(String userId) {
        // TODO 业务逻辑校验

        // 删除
        userRepository.logicDelete(userId);
        return true;
    }

    @Override
    @Cacheable(value = UpmsCacheConstants.USER_DETAILS, key = "#userId")
    public UserVO detailById(String userId) {
        // TODO 业务逻辑校验

        // 删除
        UserDO user = userRepository.findById(userId)
                .orElseThrow(() -> new RuntimeException("当前数据不存在"));
        // 转换
        return UserConverter.convertTo(user);
    }

    /**
     * 加载用户完整信息
     *
     * @param user 数据库信息
     * @return com.easy.cloud.web.service.upms.api.vo.UserVO
     */
    private UserVO loadUserAllInfo(UserDO user) {
        // 转换用户信息
        UserVO userVO = UserConverter.convertTo(user);
        // 获取部门信息
        if (Objects.nonNull(user.getDept())) {
            userVO.setDept(DeptConverter.convertTo(user.getDept()));
        }

        // 获取角色信息
        List<RoleDO> roles = user.getRoles();
        // 获取用户角色信息
        userVO.setRoleCodes(roles.stream().map(RoleDO::getCode).collect(Collectors.toSet()));
        // 设置权限
        userVO.setPermissions(roles.stream()
                .flatMap(role -> role.getMenus().stream().map(MenuDO::getPerms))
                .collect(Collectors.toSet()));
        return userVO;
    }

    @Override
    public UserVO baseDetail(String userId) {
        // 获取用户详情
        UserDO userDO = userRepository.findById(userId).orElseThrow(() -> new BusinessException("当前用户信息不存在"));
        // 加载用户所有信息
        return this.loadUserAllInfo(userDO);
    }

    @Override
    public List<UserVO> list(UserQueryDTO userQueryDTO) {
        return UserConverter.convertTo(userRepository.findAll(SpecificationWrapper.where()
                // 过滤掉超管用户
                .ne(UserDO::getUserName, GlobalCommonConstants.SUPER_ADMIN_ROLE)
                .like(StrUtil.isNotBlank(userQueryDTO.getUserName()), UserDO::getUserName, userQueryDTO.getUserName())
                .eq(StrUtil.isNotBlank(userQueryDTO.getProvince()), UserDO::getProvince, userQueryDTO.getProvince())
                .eq(StrUtil.isNotBlank(userQueryDTO.getCity()), UserDO::getCity, userQueryDTO.getCity())
                .eq(StrUtil.isNotBlank(userQueryDTO.getDistrict()), UserDO::getDistrict, userQueryDTO.getDistrict())
                .eq(StrUtil.isNotBlank(userQueryDTO.getStreet()), UserDO::getStreet, userQueryDTO.getStreet())
                .like(StrUtil.isNotBlank(userQueryDTO.getNickName()), UserDO::getNickName, userQueryDTO.getNickName())
                .like(StrUtil.isNotBlank(userQueryDTO.getRealName()), UserDO::getRealName, userQueryDTO.getRealName())
                .eq(StrUtil.isNotBlank(userQueryDTO.getIdCard()), UserDO::getIdCard, userQueryDTO.getIdCard())
                .eq(StrUtil.isNotBlank(userQueryDTO.getTel()), UserDO::getTel, userQueryDTO.getTel())
                .eq(StrUtil.isNotBlank(userQueryDTO.getEmail()), UserDO::getEmail, userQueryDTO.getEmail())));
    }

    @Override
    public Page<UserVO> page(UserQueryDTO userQueryDTO) {
        // 获取用户信息
        return UserConverter.convertTo(userRepository.findAll(SpecificationWrapper.where()
                // 过滤掉超管用户
                .ne(UserDO::getUserName, GlobalCommonConstants.SUPER_ADMIN_ROLE)
                .like(StrUtil.isNotBlank(userQueryDTO.getUserName()), UserDO::getUserName, userQueryDTO.getUserName())
                .eq(StrUtil.isNotBlank(userQueryDTO.getProvince()), UserDO::getProvince, userQueryDTO.getProvince())
                .eq(StrUtil.isNotBlank(userQueryDTO.getCity()), UserDO::getCity, userQueryDTO.getCity())
                .eq(StrUtil.isNotBlank(userQueryDTO.getDistrict()), UserDO::getDistrict, userQueryDTO.getDistrict())
                .eq(StrUtil.isNotBlank(userQueryDTO.getStreet()), UserDO::getStreet, userQueryDTO.getStreet())
                .like(StrUtil.isNotBlank(userQueryDTO.getNickName()), UserDO::getNickName, userQueryDTO.getNickName())
                .like(StrUtil.isNotBlank(userQueryDTO.getRealName()), UserDO::getRealName, userQueryDTO.getRealName())
                .eq(StrUtil.isNotBlank(userQueryDTO.getIdCard()), UserDO::getIdCard, userQueryDTO.getIdCard())
                .eq(StrUtil.isNotBlank(userQueryDTO.getTel()), UserDO::getTel, userQueryDTO.getTel())
                .eq(StrUtil.isNotBlank(userQueryDTO.getEmail()), UserDO::getEmail, userQueryDTO.getEmail()), userQueryDTO.pageable()));
    }

    @Override
    @CacheEvict(value = UpmsCacheConstants.USER_DETAILS, allEntries = true)
    public UserVO bindUserRole(UserBindDTO userBindDTO) {
        // 根据ID获取用户信息
        UserDO user = userRepository.findById(userBindDTO.getId()).orElseThrow(() -> new BusinessException("当前用户信息不存在"));
        // 转换用户信息
        UserVO userVO = UserConverter.convertTo(user);
        // 获取角色列表
        List<String> roleIds = Optional.ofNullable(userBindDTO.getRoleIds()).orElse(new ArrayList<>());
        // 绑定的角色信息为空，则
        if (CollUtil.isNotEmpty(roleIds)) {
            // 根据角色ID获取角色列表
            List<RoleDO> roles = roleRepository.findAllById(roleIds);
            // 设置角色编码
            userVO.setRoleCodes(roles.stream().map(RoleDO::getCode).collect(Collectors.toSet()));
            // 绑定角色
            user.setRoles(roles);
        }

        // 修改角色
        userRepository.save(user);
        return userVO;
    }

    @Override
    public UserVO loadLoginUserByUsername(String userName) {
        // 此时查询用户信息不需要租户
        UserDO user = TenantBroker.applyAsNoTenant(tenantId -> userRepository.findByUserName(userName))
                .orElseThrow(() -> new BusinessException(HttpResultEnum.USER_PASSWORD.getCode(), HttpResultEnum.USER_PASSWORD.getDesc()));
        // 加载用户所有信息
        UserVO userVO = this.loadUserAllInfo(user);
        // 认证需密码进行认证
        userVO.setPassword(user.getPassword());
        return userVO;
    }

    @Override
    public UserVO loadLoginUserBySocial(String type, UserLoginDTO userLoginDTO) {
        // 根据第三方登录类型获取登录方式
        SocialTypeEnum socialType = SocialTypeEnum.getSocialByType(type).orElseThrow(() -> new BusinessException("当前登录类型不支持"));
        // 获取处理类
        ISocialService socialService = socialServices.get(socialType);
        // Oauth2.0 code授权模式
        UserDO userDO = socialService.loadSocialUser(userLoginDTO);
        // 查询是否满足一下数据
        Optional<UserDO> userOptional = userRepository.findByUnionIdOrAppleIdOrTel(userDO.getUnionId(), userDO.getAppleId(), userDO.getTel())
                .stream().findFirst();
        if (userOptional.isPresent()) {
            // 获取详情
            return UserConverter.convertTo(userOptional.get());
        }

        // 存储新的数据
        userRepository.save(userDO);
        // 获取详情，走缓存
        return UserConverter.convertTo(userDO);
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    public UserVO registerUser(UserRegisterDTO userRegisterDTO) {
        return this.save(userRegisterDTO.convertTo(UserDTO.class));
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    @CacheEvict(value = UpmsCacheConstants.USER_DETAILS, allEntries = true)
    public UserVO lockUser(String userId) {
        UserDO user = userRepository.findById(userId).orElseThrow(() -> new BusinessException("用户信息不存在"));
        user.setStatus(StatusEnum.FREEZE_STATUS);
        userRepository.save(user);
        return UserConverter.convertTo(user);
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    @CacheEvict(value = UpmsCacheConstants.USER_DETAILS, allEntries = true)
    public void changePassword(UserBindDTO userBindDTO) {
        // 旧密码不能为空
        if (StrUtil.isEmpty(userBindDTO.getPassword())) {
            throw new BusinessException("原密码不能为空");
        }

        // 新密码不能为空
        if (StrUtil.isEmpty(userBindDTO.getNewPassword())) {
            throw new BusinessException("新密码不能为空");
        }

        // 新旧密码不能相同
        if (userBindDTO.getNewPassword().equals(userBindDTO.getPassword())) {
            throw new BusinessException("新旧密码不能相同");
        }

        // 获取用户信息
        UserDO user = userRepository.findById(userBindDTO.getId()).orElseThrow(() -> new BusinessException("用户信息不存在"));
        if (passwordEncoder.matches(userBindDTO.getPassword(), user.getPassword())) {
            log.error("原密码错误，修改个人信息失败:{}", userBindDTO.getId());
            throw new BusinessException("修改密码失败，输入的旧密码错误");
        }

        // 编译新密码
        String encodePassword = passwordEncoder.encode(userBindDTO.getNewPassword());
        user.setPassword(encodePassword);
        userRepository.save(user);
    }

    @Override
    public Boolean checkPassword(UserBindDTO userBindDTO) {
        // 密码不能为空
        if (StrUtil.isEmpty(userBindDTO.getPassword())) {
            throw new BusinessException("密码不能为空");
        }

        // 读取用户信息
        UserDO user = userRepository.findById(userBindDTO.getId()).orElseThrow(() -> new BusinessException("用户信息不存在"));
        return passwordEncoder.matches(userBindDTO.getPassword(), user.getPassword());
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    @CacheEvict(value = UpmsCacheConstants.USER_DETAILS, allEntries = true)
    public void bindUserTel(UserBindDTO userBindDTO) {
        // 用户电话不能为空
        if (StrUtil.isBlank(userBindDTO.getTel())) {
            throw new BusinessException("用户电话不能为空");
        }

        // 校验是否是手机号
        boolean mobile = PhoneUtil.isMobile(userBindDTO.getTel());
        if (!mobile) {
            throw new BusinessException("当前输入的手机号格式错误");
        }

        // 获取当前用户信息
        UserDO user = userRepository.findById(SecurityUtils.getAuthenticationUser().getId())
                .orElseThrow(() -> new BusinessException("请先登录"));
        // 设置电话信息
        user.setTel(userBindDTO.getTel());
        userRepository.save(user);
    }

    @Override
    public void removeTenantAllUser(String tenantId) {
        userRepository.logicDeleteTenantAllUser(tenantId);
    }

    @Override
    public void freezeTenantAllUser(String tenantId) {
        userRepository.updateTenantAllUser(tenantId, StatusEnum.FREEZE_STATUS.name());
    }
}